//========================================================================
/**
*  Copyright (c), 2007-2009.
*  @file	cx_xmlp.c
*  @author	godwinguo
*  @email	godwinguo@tencent.com
*  @brief	XML parser
*  @version	1.0.0
*  @history
*    <author>      <time>      <version>     <desc>
*  ------------ ------------  ------------  -----------------
*    godwinguo   2010/03/02     1.0.0        Create
*/
//========================================================================

#include "cx_platforms.h"
#include "cx_typedef.h"
//#include "cx_platform_base.h"
#include "cx_memory.h"
#include "cx_xmlp.h"
#include "cx_string.h"

// #include <string.h>
// #include <stdlib.h>
// #include <e32debug.h>

/*************************************************************************
* MACRO
*************************************************************************/

#if defined(__XMLP_DISCARD_INCOMPLETE_TAG__) && defined(__XMLP_AUTOFIX_INCOMPLETE_TAG__)
#include Wrong XMLP feature setting
#endif	// #if defined(__XMLP_DISCARD_INCOMPLETE_TAG__) && defined(__XMLP_AUTOFIX_INCOMPLETE_TAG__)

#define	XMLP_PREFETCH_BUFFER_SIZE		(4096)
#define	XMLP_SLICE_INCREMENT			(4096)
#define XMLP_SLICE_BUFFER_SIZE			(2048)

#define	XMLP_TAG_DATA_MAX_LEN			(200 * 1024)
#define	XMLP_TEXT_DATA_MAX_LEN			(200 * 1024)

#define XMLP_ESCAPE_SEQ_MAX_LEN			(12)

#define XMLP_ASSERT( __x )				(__x)
#define XMLP_UNUSED( __x )				((void)__x)
#define XMLP_TRACE						RDebug::Printf

#define XMLP_CALL( __fp, __args )		(*__fp)__args
#define XMLP_MIN( a, b )				( ((a)<(b)) ? (a) : (b) )

#define	XMLP_IS_WS( __c )				((__c) == ' ' || (__c) == '\t' ? 1 : 0)
#define	XMLP_IS_EOL( __c )				((__c) == '\r' || (__c) == '\n' ? 1 : 0)
#define XMLP_IS_ALPHA( __c )			( ((__c) >= 'a' && (__c) <= 'z') || ((__c) >= 'A' && (__c) <= 'Z') ? 1 : 0 )
#define XMLP_IS_DIGIT( __c )			( ((__c) >= '0' && (__c) <= '9') ? 1 : 0 )

#define XMLP_HEAP_ALLOC( __size )		cx_malloc( __size )
#define XMLP_HEAP_FREE( __p )			if ( NULL != __p ) { cx_free( __p ); __p = NULL; }

#ifndef NULL
#define NULL	(0)
#endif	// #ifndef NULL

/*************************************************************************
* Local functions
*************************************************************************/

static unsigned long	__xmlp_routine_run_once( xmlp_document_ptr_t doc_ptr );

static void				__xmlp_update_progress( xmlp_document_ptr_t doc_ptr );
static unsigned long	__xmlp_told_event( xmlp_document_ptr_t doc_ptr, xmlp_event_type_e evt_type, xmlp_node_ptr_t node_ptr, void * para );

static unsigned long	__xmlp_on_state_initial( xmlp_document_ptr_t doc_ptr );
static unsigned long	__xmlp_on_state_slice( xmlp_document_ptr_t doc_ptr );
static unsigned long	__xmlp_on_state_forward( xmlp_document_ptr_t doc_ptr );

static unsigned long	__xmlp_on_state_type_decl( xmlp_document_ptr_t doc_ptr );
static unsigned long	__xmlp_on_state_type_elem_beg( xmlp_document_ptr_t doc_ptr );
static unsigned long	__xmlp_on_state_type_elem_end( xmlp_document_ptr_t doc_ptr );
static unsigned long	__xmlp_on_state_type_text( xmlp_document_ptr_t doc_ptr );

#if defined(__XMLP_SUPPORT_NODE_DOC_TYPE__)
static unsigned long	__xmlp_on_state_type_doc_type( xmlp_document_ptr_t doc_ptr );
#endif	// #if defined(__XMLP_SUPPORT_NODE_DOC_TYPE__)

#if defined(__XMLP_SUPPORT_NODE_CDATA__)
static unsigned long	__xmlp_on_state_type_cdata( xmlp_document_ptr_t doc_ptr );
#endif	// #if defined(__XMLP_SUPPORT_NODE_CDATA__)

#if defined(__XMLP_SUPPORT_NODE_REMARK__)
static unsigned long	__xmlp_on_state_type_remark( xmlp_document_ptr_t doc_ptr );
#endif	// #if defined(__XMLP_SUPPORT_NODE_REMARK__)

static unsigned long	__xmlp_on_state_type_unknwon( xmlp_document_ptr_t doc_ptr );
static unsigned long	__xmlp_on_state_error( xmlp_document_ptr_t doc_ptr );
static unsigned long	__xmlp_on_state_finish( xmlp_document_ptr_t doc_ptr );

static unsigned char *	__xmlp_tag_name( unsigned char * str, unsigned long len, unsigned long * tag_len_ptr );
static unsigned long	__xmlp_build_attrib_list( xmlp_document_ptr_t doc_ptr, xmlp_node_ptr_t node_ptr, unsigned char * str, unsigned long len );
static unsigned long	__xmlp_link_up_node( xmlp_document_ptr_t doc_ptr, xmlp_node_ptr_t node_ptr );

static unsigned long	__xmlp_default_node_handler( xmlp_event_type_e event, xmlp_document_ptr_t doc_ptr, xmlp_node_ptr_t node_ptr, void * para );

static unsigned long	__xmlp_set_state( xmlp_document_ptr_t doc_ptr, xmlp_state_e state );
static unsigned long	__xmlp_set_error( xmlp_document_ptr_t doc_ptr, xmlp_error_e error );

#if defined(__XMLP_DEBUG__)
static void				__xmlp_routine_breakpoint( xmlp_document_ptr_t doc_ptr );
static void				__xmlp_routine_breakpoint2( xmlp_document_ptr_t doc_ptr );
#endif	//#if defined(__XMLP_DEBUG__)

static unsigned long	__xmlp_resize_slice_buffer( xmlp_document_ptr_t doc_ptr );
static void				__xmlp_free_slice_buffer( xmlp_document_ptr_t doc_ptr );
static void				__xmlp_clear_slice_buffer( xmlp_document_ptr_t doc_ptr );

static unsigned long	__xmlp_alloc_prefetch_buffer( xmlp_document_ptr_t doc_ptr );
static void				__xmlp_free_prefetch_buffer( xmlp_document_ptr_t doc_ptr );
static void				__xmlp_clear_prefetch_buffer( xmlp_document_ptr_t doc_ptr );

unsigned char	__xmlp_unescape_seq( unsigned char * esc_str, unsigned long esc_len );

static unsigned long	__xmlp_utf8_char_count( unsigned char * str, unsigned long len );
static unsigned char *	__xmlp_utf8_skip_ws( unsigned char * str, unsigned long len );
static unsigned long	__xmlp_utf8_count_ws( unsigned char * str, unsigned long len );
static unsigned long	__xmlp_utf8_strlen_ex_chr( unsigned char * str, unsigned char chr );
static unsigned long	__xmlp_utf8_strlen_ex_set( unsigned char * str, unsigned char * set );
static unsigned long	__xmlp_utf8_strncmp( unsigned char * lhs, unsigned char * rhs, unsigned long len );
static unsigned long	__xmlp_utf8_strncmp_ic( unsigned char * lhs, unsigned char * rhs, unsigned long len );
static unsigned long	__xmlp_utf8_count_ws_eol( unsigned char * str, unsigned long len );

static unsigned long	__xmlp_ucs2_strncmp( unsigned short * lhs, unsigned short * rhs, unsigned long len );
static unsigned long	__xmlp_ucs2_strncmp_ic( unsigned short * lhs, unsigned short * rhs, unsigned long len );
static unsigned long	__xmlp_ucs2_strlen_ex_chr( unsigned short * str, unsigned short chr );

static unsigned long	__xmlp_utf8_to_ucs2( void * buff_ptr, unsigned long buff_size, void * utf8_str, unsigned long utf8_len );

/*************************************************************************
* Structures
*************************************************************************/

struct __struct_xmlp_named_char
{
	unsigned char	chr;
	unsigned char  esc[7];
};

typedef struct __struct_xmlp_named_char		xmlp_named_char_t;
typedef struct __struct_xmlp_named_char *	xmlp_named_char_ptr_t;

/*************************************************************************
* Local variables
*************************************************************************/
// 
// static const xmlp_named_char_t __esc_seq_char_tab[6] =
// {
//     { '<',	(unsigned char *)"&lt;"		},
//     { '>',	(unsigned char *)"&gt;"		},
//     { '&',	(unsigned char *)"&amp;"	},
//     { '\'',	(unsigned char *)"&apos;"	},
//     { '\"',	(unsigned char *)"&quot;"	},
// 	{ ' ',	(unsigned char *)"&nbsp;"	}
// };

static const unsigned long __bpc[16] =
{
	1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 3, 0
};

/*************************************************************************
* Implementations
*************************************************************************/

/*************************************************************************
* DESCRIPTION
*	$$__FUNC_DESC__$$
*************************************************************************/
static unsigned long __xmlp_ucs2_strncmp( unsigned short * lhs, unsigned short * rhs, unsigned long len )
{
	XMLP_UNUSED( len );
	
	if ( lhs == rhs )
		return 0;

	return cx_strcmp_w( (const wchar_t *)lhs, (const wchar_t *)rhs );
}

/*************************************************************************
* DESCRIPTION
*	$$__FUNC_DESC__$$
*************************************************************************/
static unsigned long __xmlp_ucs2_strncmp_ic( unsigned short * lhs, unsigned short * rhs, unsigned long len )
{
	unsigned short		ch1;
	unsigned short		ch2;
	unsigned long	i;
	
	if ( lhs == rhs )
		return 0;

	for ( i = 0; i < len; ++i )
	{
		ch1 = lhs[i];
		ch2 = rhs[i];

		if ( ch1 == '\0' || ch2 == '\0' )
		{
			return 0;
		}
		
		ch1 = (ch1 >= 0x0041 && ch1 <= 0x005A ? ch1 + 0x0020 : ch1);
		ch2 = (ch2 >= 0x0041 && ch2 <= 0x005A ? ch2 + 0x0020 : ch2);
		
		if ( ch1 != ch2 )
		{
			return 1;
		}
	}

	return 0;
}

/*************************************************************************
* DESCRIPTION
*	$$__FUNC_DESC__$$
*************************************************************************/
static unsigned long __xmlp_utf8_to_ucs2( void * buff_ptr, unsigned long buff_size, void * utf8_str, unsigned long utf8_len )
{
	unsigned long	conv_len = 0;
	unsigned long	char_cnt = 0;
	unsigned long	char_bytes;
	unsigned char *	us_utf8_str = (unsigned char *)utf8_str;
	unsigned long	i;
	unsigned short	tmp_ch;

	if ( NULL == buff_ptr || NULL == us_utf8_str || buff_size < 2 || 0 == utf8_len )
		return 0;

	for ( i = 0; i < utf8_len; i += char_bytes )
    {
		if ( '\0' == *us_utf8_str )
			break;

        char_bytes = __bpc[((unsigned long)*us_utf8_str) >> 4];

		if ( 1 == char_bytes )
		{
			tmp_ch = (unsigned short)us_utf8_str[0];
		}
		else if ( 2 == char_bytes )
		{
			if ( '\0' != us_utf8_str[1] )
			{
				tmp_ch = ((unsigned short)(us_utf8_str[0] & 0x1F) << 6) | ((unsigned short)(us_utf8_str[1] ^ 0x80));
			}
		}
		else if ( 3 == char_bytes )
		{
			if ( '\0' != us_utf8_str[1] && '\0' != us_utf8_str[2] )
			{
				tmp_ch = ((unsigned short)(us_utf8_str[0] & 0x0F) << 12) |
						 ((unsigned short)(us_utf8_str[1] ^ 0x80) << 6) |
						 ((unsigned short)(us_utf8_str[2] ^ 0x80));
			}
		}
		else
		{
			char_cnt = 0;
			conv_len = 0;
			break;
		}

		((unsigned short *)buff_ptr)[char_cnt] = tmp_ch;
		char_cnt += 1;

		conv_len += 2; // sizeof(unsigned short)
		us_utf8_str += char_bytes;
    }

	((unsigned short *)buff_ptr)[char_cnt] = '\0';
	
    return (conv_len >> 1);
}

/*************************************************************************
* DESCRIPTION
*	$$__FUNC_DESC__$$
*************************************************************************/
static unsigned long __xmlp_utf8_char_count( unsigned char * buff, unsigned long buff_size )
{
	unsigned long	chr_cnt;
	unsigned long	offset;
	unsigned long	chr_bytes;

	if ( NULL == buff || 0 == buff_size )
		return 0;

    for ( chr_cnt = 0, offset = 0; offset < buff_size; ++chr_cnt )
    {
    	if ( '\0' == buff[offset] )
    		break;

		chr_bytes = __bpc[(buff[offset]) >> 4];
		if ( chr_bytes == 0 )
			break;

		if ( offset + chr_bytes > buff_size )
			break;
		
		offset += chr_bytes;
    }

    return chr_cnt;
}

/*************************************************************************
* FUNCTION
*  __xmlp_utf8_skip_ws
* DESCRIPTION
*
*************************************************************************/
static unsigned char * __xmlp_utf8_skip_ws( unsigned char * str, unsigned long len )
{
	str += __xmlp_utf8_count_ws( str, len );
	return str;
}

/*************************************************************************
* FUNCTION
*  __xmlp_utf8_count_ws
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_utf8_count_ws( unsigned char * str, unsigned long len )
{
	unsigned long ret_val;

	XMLP_ASSERT( NULL != str );
	XMLP_ASSERT( len > 0 );

	for ( ret_val = 0; ret_val < len; ++str, ++ret_val )
	{
		if ( *str == '\0' )
			break;

		if ( 0 == XMLP_IS_WS(*str) )
			break;
	}

	return ret_val;
}

/*************************************************************************
* FUNCTION
*  __xmlp_strlen_ex_chr
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_utf8_strlen_ex_chr( unsigned char * str, unsigned char chr )
{
	unsigned long ret_val;
	
	for ( ret_val = 0; (*str) != chr && (*str) != '\0';	str++, ret_val++ )
        ;

	return ret_val;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_utf8_strncmp( unsigned char * lhs, unsigned char * rhs, unsigned long len )
{
	XMLP_ASSERT( NULL != lhs );
	XMLP_ASSERT( NULL != rhs );
	
	if ( lhs == rhs )
		return 1;

	return (unsigned long)strncmp( (const char *)lhs, (const char *)rhs, (size_t)len );
}

/*************************************************************************
* FUNCTION
*  __xmlp_strlen_ex_set
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_utf8_strlen_ex_set( unsigned char * str, unsigned char * set )
{
	unsigned long ret_val;

	for ( ret_val = 0; NULL == strchr((const char *)set, (int)(*str)) && (*str) != '\0';	str++, ret_val++ )
        ;

	return ret_val;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_utf8_strncmp_ic( unsigned char * lhs, unsigned char * rhs, unsigned long len )
{
	unsigned char	ch1;
	unsigned char	ch2;
	unsigned long	i;

	XMLP_ASSERT( NULL != lhs );
	XMLP_ASSERT( NULL != rhs );
	
	if ( lhs == rhs )
		return 0;

	for ( i = 0; i < len; ++i )
	{
		ch1 = lhs[i];
		ch2 = rhs[i];

		if ( ch1 == '\0' || ch2 == '\0' )
		{
			return 0;
		}

		ch1 = (ch1 >= 0x41 && ch1 <= 0x5A ? ch1 + 0x20 : ch1);
		ch2 = (ch2 >= 0x41 && ch2 <= 0x5A ? ch2 + 0x20 : ch2);

		if ( ch1 != ch2 )
		{
			return 1;
		}
	}

	return 0;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_utf8_count_ws_eol( unsigned char * str, unsigned long len )
{
	unsigned long ret_val;

	XMLP_ASSERT( NULL != str );
	XMLP_ASSERT( len > 0 );

	for ( ret_val = 0; ret_val < len; ++str, ++ret_val )
	{
		if ( *str == '\0' )
			break;

		if ( 0 == XMLP_IS_WS(*str) && 0 == XMLP_IS_EOL(*str) )
			break;
	}

	return ret_val;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_ucs2_strlen_ex_chr( unsigned short * str, unsigned short chr )
{
	unsigned long ret_val;
	
	for ( ret_val = 0; (*str) != chr && (*str) != '\0';	str++, ret_val++ )
        ;

	return ret_val;
}

/*************************************************************************
* FUNCTION
*  __html_page_create_widget
* DESCRIPTION
*
*************************************************************************/
unsigned char __xmlp_unescape_seq( unsigned char * esc_str, unsigned long esc_len )
{
	unsigned long	i;
	unsigned long	max;
	xmlp_named_char_t __esc_seq_char_tab[6] =
	{
		{ '<',	"&lt;"		},
		{ '>',	"&gt;"		},
		{ '&',	"&amp;"	},
		{ '\'',	"&apos;"	},
		{ '\"',	"&quot;"	},
		{ ' ',	"&nbsp;"	}
	};
	
//	CX_ASSERT( NULL != esc_str );
//	CX_ASSERT( esc_len > 0 );
	
	if ( esc_len > XMLP_ESCAPE_SEQ_MAX_LEN )
		return '\0';

	if ( esc_str[0] != '&' || esc_str[esc_len - 1] != ';' )
		return '\0';

	max = (unsigned long)( sizeof(__esc_seq_char_tab) / sizeof(__esc_seq_char_tab[0]) );
	for ( i = 0; i < max; ++i )
	{
		if ( 0 == strncmp( (const char *)__esc_seq_char_tab[i].esc, (const char *)esc_str, esc_len ) )
		{
			return __esc_seq_char_tab[i].chr;
		}
	}
	
	return '\0';
}

/*************************************************************************
* FUNCTION
*  __xmlp_update_progress
* DESCRIPTION
*
*************************************************************************/
static void __xmlp_update_progress( xmlp_document_ptr_t doc_ptr )
{
	unsigned long percent;
	unsigned long cursor;
	unsigned long size;

	if ( XMLP_SOURCE_FILE == doc_ptr->source )
	{
		cursor	= cx_file_tell( doc_ptr->stream.file );
		size	= cx_file_size( doc_ptr->stream.file );
	}
	else if ( XMLP_SOURCE_MEMORY == doc_ptr->source )
	{
		cursor	= doc_ptr->pref_cursor;
		size	= doc_ptr->pref_buff_size;
	}

	percent	= (unsigned long)( ((float)(cursor * 1.0) / (float)(size * 1.0)) * 100 );
	__xmlp_told_event( doc_ptr, xmlp_event_progress_update, NULL, (void *)percent );
}

/*************************************************************************
* FUNCTION
*  __xmlp_told_event
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_told_event( xmlp_document_ptr_t doc_ptr, xmlp_event_type_e evt_type, xmlp_node_ptr_t node_ptr, void * para )
{
	unsigned long ret_val = 0;

	XMLP_ASSERT( NULL != doc_ptr );

	if ( NULL != doc_ptr->event_hdlr )
	{
		ret_val = XMLP_CALL( doc_ptr->event_hdlr, (evt_type, doc_ptr, node_ptr, para) );
	}

	return ret_val;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_default_node_handler( xmlp_event_type_e event, xmlp_document_ptr_t doc_ptr, xmlp_node_ptr_t node_ptr, void * para )
{
	XMLP_UNUSED( event );
	XMLP_UNUSED( doc_ptr );
	XMLP_UNUSED( node_ptr );
	XMLP_UNUSED( para );

	return 1;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
static unsigned long	__xmlp_set_state( xmlp_document_ptr_t doc_ptr, xmlp_state_e state )
{
	XMLP_ASSERT( NULL != doc_ptr );
	doc_ptr->state = state;
	return 1;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
static unsigned long	__xmlp_set_error( xmlp_document_ptr_t doc_ptr, xmlp_error_e error )
{
	XMLP_ASSERT( NULL != doc_ptr );
	doc_ptr->error = error;
	return 1;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_beg
* DESCRIPTION
*
*************************************************************************/
static unsigned char * __xmlp_tag_name( unsigned char * str, unsigned long len, unsigned long * tag_len_ptr )
{
	unsigned char *	tag_name;
	unsigned long	tag_len;

	XMLP_ASSERT( NULL != str );

	tag_name = __xmlp_utf8_skip_ws( str, len );
	if ( tag_name[0] == '\0' )
		goto __FAILED;

	tag_len = __xmlp_utf8_strlen_ex_set( tag_name, (unsigned char *)" \t\r\n/>" );

	if ( NULL != tag_len_ptr )
		(*tag_len_ptr) = tag_len;
	return tag_name;

__FAILED:
	if ( NULL != tag_len_ptr)
		(*tag_len_ptr) = 0;
	return NULL;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_beg
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_build_attrib_list( xmlp_document_ptr_t doc_ptr, xmlp_node_ptr_t node_ptr, unsigned char * src_str, unsigned long src_len )
{
	xmlp_attrib_ptr_t	attr_list = NULL;
	unsigned long		attr_num = 0;
	xmlp_attrib_ptr_t	curr_attr = NULL;

	unsigned char *		name_str = NULL;
	unsigned long		name_len = 0;
	unsigned char *		value_str = NULL;
	unsigned long		value_len = 0;

	unsigned char *		surrnd_str = NULL;
	unsigned char *		equal_str = NULL;

	unsigned char		l_surround_chr;

    for ( ;; )
    {
		if ( src_str[0] == '\0' || src_len == 0 )
			break;

		name_str = __xmlp_utf8_skip_ws( src_str, src_len );
		if ( name_str[0] == '\0' )
		{
			__xmlp_set_error( doc_ptr, xmlp_error_format );
			goto __FAILED;
		}
		else if ( name_str[0] == '>' )
		{
			// paired tag
			break;
		}
		else if ( 0 == __xmlp_utf8_strncmp_ic(name_str, (unsigned char *)"/>", 2) )
		{
			// non-paired
			break;
		}
		else
		{
			if ( 0 == XMLP_IS_ALPHA(name_str[0]) )
			{
				__xmlp_set_error( doc_ptr, xmlp_error_invalid_name );
				goto __FAILED;
			}

			// dispose as an attribute
			name_len = __xmlp_utf8_strlen_ex_set( name_str, (unsigned char *)" \t=" );
			if ( name_len == 0 )
			{
				__xmlp_set_error( doc_ptr, xmlp_error_format );
				goto __FAILED;
			}

			equal_str = name_str + name_len;
			equal_str = __xmlp_utf8_skip_ws( equal_str, src_len - (equal_str - src_str) );
			if ( equal_str[0] != '=' )
			{
				__xmlp_set_error( doc_ptr, xmlp_error_format );
				goto __FAILED;
			}

			surrnd_str = equal_str + 1;
			surrnd_str = __xmlp_utf8_skip_ws( surrnd_str, src_len - (surrnd_str - src_str) );

			l_surround_chr = surrnd_str[0];
			if ( l_surround_chr == '\'' || l_surround_chr == '\"' )
			{
				value_str = __xmlp_utf8_skip_ws( surrnd_str + 1, src_len - ((surrnd_str + 1) - src_str) );
				value_len = __xmlp_utf8_strlen_ex_chr( value_str, l_surround_chr );
			}
			else
			{
#if defined(__XMLP_ALLOW_ATTRIB_VALUE_NO_SURROUND_QUOT__)
				value_str = __xmlp_utf8_skip_ws( surrnd_str, src_len - (surrnd_str - src_str) );
				value_len = __xmlp_utf8_strlen_ex_set( value_str, (unsigned char *)" \t>" );
#else	// #if defined(__XMLP_ALLOW_ATTRIB_VALUE_NO_SURROUND_QUOT__)
				__xmlp_set_error( doc_ptr, xmlp_error_format );
				goto __FAILED;
#endif	// #if defined(__XMLP_ALLOW_ATTRIB_VALUE_NO_SURROUND_QUOT__)
			}
			
#ifndef __XMLP_ALLOW_ATTRIB_VALUE_EMPTY__
			if ( value_len == 0 )
			{
				__xmlp_set_error( doc_ptr, xmlp_error_format );
				goto __FAILED;
			}
#endif	// #ifndef __XMLP_ALLOW_ATTRIB_VALUE_EMPTY__

			curr_attr = xmlp_attrib_create();
			if ( NULL == curr_attr )
			{
				__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
				goto __FAILED;
			}

			curr_attr->name	= xmlp_string_build_ex( name_str, name_len, XMLP_ENCODING_UTF8, XMLP_ENCODING_UTF8 );
			if ( NULL == curr_attr->name )
			{
				__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
				goto __FAILED;
			}

#if defined(__XMLP_ALLOW_ATTRIB_VALUE_EMPTY__)
			if ( value_len > 0 )
#endif	// #if defined(__XMLP_ALLOW_ATTRIB_VALUE_EMPTY__
			{
				curr_attr->value = xmlp_string_build_ex( value_str, value_len, __XMLP_VALUE_ENCODING__, XMLP_ENCODING_UTF8 );
				if ( NULL == curr_attr->value )
				{
					__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
					goto __FAILED;
				}
			}

			curr_attr->next = attr_list;
			attr_list = curr_attr;
			curr_attr = NULL;

			attr_num++;

			// TODO: debug here, add watch
			src_len = src_len - ((value_str + value_len + 1) - src_str);
			src_str = value_str + value_len + 1;
		}
	}

	node_ptr->first_attrib = attr_list;
	node_ptr->attrib_num = attr_num;
	return 1;

__FAILED:
	xmlp_attrib_free( curr_attr );
	xmlp_attrib_free_list( attr_list );
	return 0;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
xmlp_document_ptr_t xmlp_doc_open_file( unsigned short * file_name )
{
	xmlp_document_ptr_t	doc_ptr = NULL;

	XMLP_ASSERT( NULL != file_name );

	// Allocates document object
	doc_ptr = (xmlp_document_ptr_t)XMLP_HEAP_ALLOC( sizeof(xmlp_document_t) );
	if ( NULL == doc_ptr )
		return NULL;

	memset( doc_ptr, 0x0, sizeof(xmlp_document_t) );

	// Try to open source file
	doc_ptr->source = XMLP_SOURCE_FILE;
	doc_ptr->stream.file = cx_file_open( (CONST QTCHAR *)file_name, modeRead );
	if ( QFALSE == cx_file_isValid( doc_ptr->stream.file ) )
		goto __FAILED;
	__xmlp_set_error( doc_ptr, xmlp_error_OK );
	__xmlp_set_state( doc_ptr, xmlp_state_opened );

	xmlp_doc_set_mode( doc_ptr, xmlp_mode_DOM );
	xmlp_doc_set_handler( doc_ptr, __xmlp_default_node_handler );

	doc_ptr->opened	= 1;
	return doc_ptr;

__FAILED:
	xmlp_doc_close( doc_ptr );
	return NULL;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
xmlp_document_ptr_t xmlp_doc_open_bytes( xmlp_raw_data_ptr_t byte_data )
{
	xmlp_document_ptr_t	doc_ptr = NULL;
	
	XMLP_ASSERT( NULL != byte_data );
	XMLP_ASSERT( NULL != byte_data->addr && byte_data->size > 0 );
	
	// Allocates document object
	doc_ptr = (xmlp_document_ptr_t)XMLP_HEAP_ALLOC( sizeof(xmlp_document_t) );
	if ( NULL == doc_ptr )
		return NULL;
	
	memset( doc_ptr, 0x0, sizeof(xmlp_document_t) );
	
	// Try to open source file
	doc_ptr->source = XMLP_SOURCE_MEMORY;
	doc_ptr->stream.mem.addr = byte_data->addr;
	doc_ptr->stream.mem.size = byte_data->size;

	__xmlp_set_error( doc_ptr, xmlp_error_OK );
	__xmlp_set_state( doc_ptr, xmlp_state_opened );
	
	xmlp_doc_set_mode( doc_ptr, xmlp_mode_DOM );
	xmlp_doc_set_handler( doc_ptr, __xmlp_default_node_handler );
	
	doc_ptr->opened	= 1;
	return doc_ptr;
	
__FAILED:
	xmlp_doc_close( doc_ptr );
	return NULL;
}

/*************************************************************************
* FUNCTION
*  xmlp_doc_close
* DESCRIPTION
*
*************************************************************************/
void xmlp_doc_close( xmlp_document_ptr_t doc_ptr )
{
	if ( NULL != doc_ptr )
	{
		if ( XMLP_SOURCE_FILE == doc_ptr->source )
		{
			cx_file_close( &doc_ptr->stream.file );
			XMLP_HEAP_FREE( doc_ptr->pref_buff );
		}
		else if ( XMLP_SOURCE_MEMORY == doc_ptr->source )
		{
		}

		XMLP_HEAP_FREE( doc_ptr->slice_buff );

		xmlp_node_free_branch( doc_ptr->first_decl );
		doc_ptr->first_decl = NULL;

		xmlp_node_free_branch( doc_ptr->root_node );
		doc_ptr->root_node = NULL;

		if ( NULL != doc_ptr->user_free )
		{
			XMLP_CALL( doc_ptr->user_free, (doc_ptr->user_data) );
		}

		XMLP_HEAP_FREE( doc_ptr );
	}
}

/*************************************************************************
* FUNCTION
*  xmlp_doc_start
* DESCRIPTION
*
*************************************************************************/
unsigned long xmlp_doc_start( xmlp_document_ptr_t doc_ptr )
{
	unsigned long	ret_val;

	if ( NULL == doc_ptr )
		return 0;

	switch ( doc_ptr->state )
	{
	case xmlp_state_opened:
		{
			// new round
			__xmlp_set_state( doc_ptr, xmlp_state_initial );
			ret_val = __xmlp_routine_run_once( doc_ptr );
		}
		break;

	default:
		{
			// not supported
			__xmlp_set_error( doc_ptr, xmlp_error_internal );
			ret_val = 0;
		}
		break;
	}

	return ret_val;
}

/*************************************************************************
* FUNCTION
*  xmlp_doc_set_mode
* DESCRIPTION
*
*************************************************************************/
unsigned long xmlp_doc_set_mode( xmlp_document_ptr_t doc_ptr, xmlp_mode_e mode )
{
	if ( NULL == doc_ptr )
		return 0;
	if ( doc_ptr->state != xmlp_state_opened )
		return 0;

	doc_ptr->mode = mode;
	return 1;
}

/*************************************************************************
* FUNCTION
*  xmlp_doc_set_mode
* DESCRIPTION
*
*************************************************************************/
unsigned long xmlp_doc_set_handler( xmlp_document_ptr_t doc_ptr, xmlp_event_hdlr_t evt_hdlr )
{
	if ( NULL == doc_ptr )
		return 0;

#ifndef	__XMLP_ALLOW_PARSING_TIME_CHANGE_SETTING__
	if ( doc_ptr->state != xmlp_state_opened )
		return 0;
#endif	// #ifndef	__XMLP_ALLOW_PARSING_TIME_CHANGE_SETTING__
	
	doc_ptr->event_hdlr = (NULL == evt_hdlr ? __xmlp_default_node_handler : evt_hdlr);
	return 1;
}

/*************************************************************************
* FUNCTION
*  xmlp_doc_set_mode
* DESCRIPTION
*
*************************************************************************/
unsigned long xmlp_doc_set_user_data( xmlp_document_ptr_t doc_ptr, void * user_data, xmlp_free_fptr_t user_free )
{
	if ( NULL == doc_ptr )
		return 0;

	if ( NULL != doc_ptr->user_free )
	{
		XMLP_CALL( doc_ptr->user_free, (doc_ptr->user_data) );
		doc_ptr->user_free = NULL;
	}

	doc_ptr->user_data	= user_data;
	doc_ptr->user_free	= user_free;
	return 1;
}

/*************************************************************************
* FUNCTION
*  xmlp_doc_set_mode
* DESCRIPTION
*
*************************************************************************/
void * xmlp_doc_get_user_data( xmlp_document_ptr_t doc_ptr )
{
	XMLP_ASSERT( NULL != doc_ptr );
	return doc_ptr->user_data;
}

/*************************************************************************
* FUNCTION
*  xmlp_mark_unfreeable_branch
* DESCRIPTION
*
*************************************************************************/
void xmlp_mark_unfreeable_branch( xmlp_node_ptr_t branch_ptr )
{
	while ( NULL != branch_ptr )
	{
		branch_ptr->unfree_cnt++;
		branch_ptr = branch_ptr->parent;
	}
}

/*************************************************************************
* FUNCTION
*  xmlp_mark_freeable_branch
* DESCRIPTION
*
*************************************************************************/
void xmlp_mark_freeable_branch( xmlp_node_ptr_t branch_ptr )
{
	while ( NULL != branch_ptr )
	{
		branch_ptr->unfree_cnt--;
		branch_ptr = branch_ptr->parent;
	}
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
xmlp_node_ptr_t	xmlp_node_create( void )
{
	xmlp_node_ptr_t	node_ptr = NULL;
	node_ptr = (xmlp_node_ptr_t)XMLP_HEAP_ALLOC( sizeof(xmlp_node_t) );
	if ( NULL == node_ptr )
		return NULL;

	memset( node_ptr, 0x0, sizeof(xmlp_node_t) );
	node_ptr->type		= xmlp_node_type_unknown;
	node_ptr->text_type	= xmlp_text_type_none;
	node_ptr->is_paired = 0;

	return node_ptr;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
void xmlp_node_free( xmlp_node_ptr_t node_ptr )
{
	if ( NULL == node_ptr )
		return;

	if ( node_ptr->unfree_cnt > 0 )
		node_ptr->unfree_cnt--;

	if ( 0 == node_ptr->unfree_cnt )
	{
		xmlp_attrib_free_list( node_ptr->first_attrib );
		node_ptr->first_attrib = NULL;

		XMLP_STRING_FREE( node_ptr->name );

		if ( node_ptr->text_type == xmlp_text_type_string )
		{
			XMLP_STRING_FREE( node_ptr->text_data.text );
		}
		else if ( node_ptr->text_type == xmlp_text_type_raw_safe )
		{
			XMLP_HEAP_FREE( node_ptr->text_data.raw.addr );
			node_ptr->text_data.raw.size = 0;
		}

		xmlp_node_free_user_data( node_ptr );
		XMLP_HEAP_FREE( node_ptr );
	}
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
void xmlp_node_free_branch( xmlp_node_ptr_t branch_ptr )
{
	if ( NULL == branch_ptr )
		return;

	xmlp_node_free_branch( branch_ptr->first_child );
	branch_ptr->first_child = NULL;

	xmlp_node_free_branch( branch_ptr->next_sibling );
	branch_ptr->next_sibling = NULL;

	xmlp_node_free( branch_ptr );
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
xmlp_attrib_ptr_t xmlp_attrib_create( void )
{
	xmlp_attrib_ptr_t	attrib_ptr = NULL;
	attrib_ptr = (xmlp_attrib_ptr_t)XMLP_HEAP_ALLOC( sizeof(xmlp_attrib_t) );
	if ( NULL == attrib_ptr )
		return NULL;
	
	memset( attrib_ptr, 0x0, sizeof(xmlp_attrib_t) );
	return attrib_ptr;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
void xmlp_attrib_free( xmlp_attrib_ptr_t attrib_ptr )
{
	if ( NULL != attrib_ptr )
	{
		XMLP_STRING_FREE( attrib_ptr->name );
		XMLP_STRING_FREE( attrib_ptr->value );
		XMLP_HEAP_FREE( attrib_ptr );
	}
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
void xmlp_attrib_free_list( xmlp_attrib_ptr_t first_attrib_ptr )
{
	xmlp_attrib_ptr_t next_ptr;
	while ( NULL != first_attrib_ptr )
	{
		next_ptr = first_attrib_ptr->next;
		xmlp_attrib_free( first_attrib_ptr );
		first_attrib_ptr = next_ptr;
	}
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
#if defined(__XMLP_DEBUG__)
static void __xmlp_routine_breakpoint( xmlp_document_ptr_t doc_ptr )
{
    XMLP_UNUSED( doc_ptr );
    //XMLP_TRACE( "[XMLP]: parse state changed into, %d\n", doc_ptr->state );
}
#endif  // #if defined(__XMLP_DEBUG__)

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
#if defined(__XMLP_DEBUG__)
static void __xmlp_routine_breakpoint2( xmlp_document_ptr_t doc_ptr )
{
    XMLP_UNUSED( doc_ptr );
}
#endif	// #if defined(__XMLP_DEBUG__)

/*************************************************************************
* FUNCTION
*  __xmlp_routine_run_once
* DESCRIPTION
*	XML parse main routine
*************************************************************************/
static unsigned long __xmlp_routine_run_once( xmlp_document_ptr_t doc_ptr )
{
	unsigned long	func_ret;
	unsigned long	cont_flag = 1;

	XMLP_ASSERT( NULL != doc_ptr );

	func_ret = 0;
	while ( 0 != cont_flag )
	{
#if defined(__XMLP_DEBUG__)
		__xmlp_routine_breakpoint( doc_ptr );
#endif	//#if defined(__XMLP_DEBUG__)

		switch ( doc_ptr->state )
		{
		case xmlp_state_initial:
			{
				__xmlp_on_state_initial( doc_ptr );
			}
			break;

		case xmlp_state_slice:
			{
				__xmlp_on_state_slice( doc_ptr );
			}
			break;

		case xmlp_state_forward:
			__xmlp_on_state_forward( doc_ptr );
			break;

#if defined(__XMLP_SUPPORT_NODE_DOC_TYPE__)
		case xmlp_state_doc_type:
			__xmlp_on_state_type_doc_type( doc_ptr );
			break;
#endif	// #if defined(__XMLP_SUPPORT_NODE_DOC_TYPE__)

		case xmlp_state_type_decl:		// <?XML xxx yyy zzz>
			__xmlp_on_state_type_decl( doc_ptr );
			break;

		case xmlp_state_type_elem_beg:	// <TAG xxx yyy zzz> or <TAG xxx yyy zzz />
			__xmlp_on_state_type_elem_beg( doc_ptr );
			break;

		case xmlp_state_type_elem_end:	// </TAG>
			__xmlp_on_state_type_elem_end( doc_ptr );
			break;

		case xmlp_state_type_text:		// <xxx>THIS IS AN APPLE</xxx>
			__xmlp_on_state_type_text( doc_ptr );
			break;

#if defined(__XMLP_SUPPORT_NODE_CDATA__)
		case xmlp_state_type_cdata:		// <![CDATA[ xxx ]]>
			__xmlp_on_state_type_cdata( doc_ptr );
			break;
#endif	// #if defined(__XMLP_SUPPORT_NODE_CDATA__)

#if defined(__XMLP_SUPPORT_NODE_REMARK__)
		case xmlp_state_type_remark:	// <!-- xxx -->
			__xmlp_on_state_type_remark( doc_ptr );
			break;
#endif	// #if defined(__XMLP_SUPPORT_NODE_REMARK__)

		case xmlp_state_type_unknown:	// Unknown type
			__xmlp_on_state_type_unknwon( doc_ptr );
			break;

		case xmlp_state_error:
			__xmlp_on_state_error( doc_ptr );
			break;

		case xmlp_state_finish:
			func_ret	= __xmlp_on_state_finish( doc_ptr );
			cont_flag	= 0;
			break;

		default:	// never get here
			XMLP_ASSERT( 0 );
			break;
		}

#if defined(__XMLP_DEBUG__)
		__xmlp_routine_breakpoint2( doc_ptr );
#endif	//#if defined(__XMLP_DEBUG__)
	}

	return func_ret;
}

/*************************************************************************
* FUNCTION
*  __xmlp_routine_run_once
* DESCRIPTION
*	XML parse main routine
*************************************************************************/
static unsigned long __xmlp_resize_slice_buffer( xmlp_document_ptr_t doc_ptr )
{
	unsigned long	new_size = 0;
	unsigned char *	new_buff = NULL;

	XMLP_ASSERT( NULL != doc_ptr );

	new_size = doc_ptr->slice_buff_size + XMLP_SLICE_INCREMENT;	
	new_buff = (unsigned char *)XMLP_HEAP_ALLOC( new_size + 2 );
	if ( NULL == new_buff )
		return 0;

	memcpy( new_buff, doc_ptr->slice_buff, doc_ptr->slice_len );
	__xmlp_free_slice_buffer( doc_ptr );

	doc_ptr->slice_buff = new_buff;
	doc_ptr->slice_buff[doc_ptr->slice_len] = '\0';
	doc_ptr->slice_buff_size = new_size;
	return 1;
}

/*************************************************************************
* FUNCTION
*  __xmlp_routine_run_once
* DESCRIPTION
*	XML parse main routine
*************************************************************************/
static void __xmlp_free_slice_buffer( xmlp_document_ptr_t doc_ptr )
{
	if ( NULL != doc_ptr )
	{
		XMLP_HEAP_FREE( doc_ptr->slice_buff );
	}
}

/*************************************************************************
* FUNCTION
*  __xmlp_routine_run_once
* DESCRIPTION
*	XML parse main routine
*************************************************************************/
static unsigned long __xmlp_alloc_prefetch_buffer( xmlp_document_ptr_t doc_ptr )
{
	XMLP_ASSERT( NULL != doc_ptr );

	if ( XMLP_SOURCE_FILE == doc_ptr->source )
	{
		__xmlp_free_prefetch_buffer( doc_ptr );
		doc_ptr->pref_buff = (unsigned char *)XMLP_HEAP_ALLOC( XMLP_PREFETCH_BUFFER_SIZE + 2 );
		if ( NULL == doc_ptr->pref_buff )
		{
			cx_debug_trace("NULL == doc_ptr->pref_buff=%d",XMLP_PREFETCH_BUFFER_SIZE+2);
			return 0;
		}

		
		doc_ptr->pref_buff_size	= XMLP_PREFETCH_BUFFER_SIZE;
		doc_ptr->pref_len		= 0;
	}
	else if ( XMLP_SOURCE_MEMORY == doc_ptr->source )
	{
		doc_ptr->pref_buff		= (unsigned char *)doc_ptr->stream.mem.addr;
		doc_ptr->pref_buff_size	= doc_ptr->stream.mem.size;
		doc_ptr->pref_len		= doc_ptr->stream.mem.size;
	}

	return 1;
}

/*************************************************************************
* FUNCTION
*  __xmlp_routine_run_once
* DESCRIPTION
*	XML parse main routine
*************************************************************************/
static void __xmlp_free_prefetch_buffer( xmlp_document_ptr_t doc_ptr )
{
	if ( NULL != doc_ptr )
	{
		if ( XMLP_SOURCE_FILE == doc_ptr->source )
		{
			XMLP_HEAP_FREE( doc_ptr->pref_buff );
		}
	}
}

/*************************************************************************
* FUNCTION
*  __xmlp_routine_run_once
* DESCRIPTION
*	XML parse main routine
*************************************************************************/
static unsigned long __xmlp_on_state_initial( xmlp_document_ptr_t doc_ptr )
{
	unsigned long	ret_val;
	
	XMLP_ASSERT( NULL != doc_ptr );

	// allocates pre-fetching buffer
	ret_val = __xmlp_alloc_prefetch_buffer( doc_ptr );
	if ( 0 == ret_val )
	{

		__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
		goto __FAILED;
	}
	// allocates slice buffer
	ret_val = __xmlp_resize_slice_buffer( doc_ptr );
	if ( 0 == ret_val )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
		goto __FAILED;
	}
	ret_val = __xmlp_told_event( doc_ptr, xmlp_event_initial, NULL, NULL );
	if ( 0 == ret_val )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_user_error );
		goto __FAILED;
	}

	__xmlp_set_state( doc_ptr, xmlp_state_slice );
	return 1;

__FAILED:
	__xmlp_set_state( doc_ptr, xmlp_state_error );
	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_routine_run_once
* DESCRIPTION
*	XML parse main routine
*************************************************************************/
void xmlp_doc_break( xmlp_document_ptr_t doc_ptr )
{
	if ( NULL == doc_ptr )
		return;

	__xmlp_set_error( doc_ptr, xmlp_error_user_break );
}

/*************************************************************************
* FUNCTION
*  __xmlp_routine_run_once
* DESCRIPTION
*	XML parse main routine
*************************************************************************/
static void __xmlp_clear_slice_buffer( xmlp_document_ptr_t doc_ptr )
{
	XMLP_ASSERT( NULL != doc_ptr );
	
	if ( NULL != doc_ptr->slice_buff )
		doc_ptr->slice_buff[0] = '\0';
	
	doc_ptr->slice_len		= 0;
	doc_ptr->slice_is_tag	= 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_routine_run_once
* DESCRIPTION
*	XML parse main routine
*************************************************************************/
static void __xmlp_clear_prefetch_buffer( xmlp_document_ptr_t doc_ptr )
{
	XMLP_ASSERT( NULL != doc_ptr );
	
	if ( NULL != doc_ptr->pref_buff )
		doc_ptr->pref_buff[0] = '\0';

	doc_ptr->pref_cursor	= 0;
	doc_ptr->pref_len		= 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_routine_run_once
* DESCRIPTION
*	XML parse main routine
*************************************************************************/
static unsigned long __xmlp_on_state_slice( xmlp_document_ptr_t doc_ptr )
{
	unsigned char	ch_tmp;
	unsigned char	ch_beg;
	unsigned char	ch_end;
	unsigned long	limit_len;

	unsigned long	ret_val;
	unsigned long	inside_tag = 0;

	unsigned char *	esc_str = NULL;
	unsigned long	esc_len = 0;
	unsigned char	tmp_chr;
	__xmlp_clear_slice_buffer( doc_ptr );


// Step 1
	for ( ;; )
	{
		// cursor reaches end of pre-fetching buffer
		if ( doc_ptr->pref_cursor >= doc_ptr->pref_len )
		{
			__xmlp_update_progress( doc_ptr );

			if ( XMLP_SOURCE_FILE == doc_ptr->source )
			{
				QINT read_bytes;
				__xmlp_clear_prefetch_buffer( doc_ptr );
				
				read_bytes = cx_file_read( doc_ptr->stream.file, doc_ptr->pref_buff, doc_ptr->pref_buff_size );

				if ( read_bytes < 0 )
				{
					__xmlp_set_error( doc_ptr, xmlp_error_internal );
					goto __FAILED;
				}
				if ( 0 == read_bytes )
					goto __FINISH;

				doc_ptr->pref_len = (unsigned long)read_bytes;
			}
			else if ( XMLP_SOURCE_MEMORY == doc_ptr->source )
			{
				goto __FINISH;
			}

			// skip the heading white characters
			doc_ptr->pref_cursor = __xmlp_utf8_count_ws_eol( (unsigned char *)doc_ptr->pref_buff, doc_ptr->pref_len );
			if ( doc_ptr->pref_cursor >= doc_ptr->pref_len )
				continue;
		}
 
		ch_tmp = doc_ptr->pref_buff[doc_ptr->pref_cursor];
		if ( 0 == XMLP_IS_WS(ch_tmp) && 0 == XMLP_IS_EOL(ch_tmp) )
		{
			// choose terminator
			if ( ch_tmp == '<' )
			{
				// The first available character is '<' that indicates the cursor is locating in a <tag>
				// So, we need to completely slice the characters into slice_buff where between '<' and '>'
				ch_end		= '>';
				ch_beg		= '<';
				inside_tag	= 1;
				limit_len	= XMLP_TAG_DATA_MAX_LEN;
			}
			else
			{
				// Slice remaining characters into slice_buff until meet '<'. Perhaps, it's a TEXT node or something else.
				ch_end		= '<';
				ch_beg		= '\0';
				inside_tag	= 0;
				limit_len	= XMLP_TEXT_DATA_MAX_LEN;
			}
			break;
		}

		doc_ptr->pref_cursor++;
	}

// Step 2
	for ( ;; )
	{
		// cursor reaches end of pre-fetching buffer
		if ( doc_ptr->pref_cursor >= doc_ptr->pref_len )
		{
			__xmlp_update_progress( doc_ptr );
			
			if ( XMLP_SOURCE_FILE == doc_ptr->source )
			{
				QINT read_bytes;
				
				__xmlp_clear_prefetch_buffer( doc_ptr );

				read_bytes = cx_file_read( doc_ptr->stream.file, doc_ptr->pref_buff, doc_ptr->pref_buff_size );
				if ( read_bytes < 0 )
				{
					__xmlp_set_error( doc_ptr, xmlp_error_internal );
					goto __FAILED;
				}
				
				if ( 0 == read_bytes )
					goto __FINISH;
				
				doc_ptr->pref_len = (unsigned long)read_bytes;
			}
			else if ( XMLP_SOURCE_MEMORY == doc_ptr->source )
			{
				goto __FINISH;
			}
		}

		ch_tmp = doc_ptr->pref_buff[doc_ptr->pref_cursor];

		// Try to unescape the characters
		if ( 0 == inside_tag )
		{
			if ( ch_tmp == 0x26 )
			{
				esc_str	= &doc_ptr->pref_buff[doc_ptr->pref_cursor];
				esc_len	= 0;
			}
			
			if ( NULL != esc_str )
			{
				esc_len++;
				
				if ( ch_tmp == 0x3B )
				{
					/*if ( esc_str[1] == '#' )
					{
						unsigned long conv_val = (unsigned long)0x20;

						if ( esc_str[2] == 'x' || esc_str[2] == 'X' )	// is hex number
						{
							esc_str[esc_len - 1] = '\0';
							conv_val = __xmlp_utf8_hex_to_uint32( (unsigned char *)(esc_str + 2) );
						}
						else
						{
							esc_str[esc_len - 1] = '\0';
							conv_val = __xmlp_utf8_hex_to_uint32( (unsigned char *)(esc_str + 1) );
						}

						if ( conv_val <= 0x00FF )
						{
							doc_ptr->slice_buff[doc_ptr->slice_len++] = (unsigned char)conv_val;
						}
						else if ( conv_val <= 0xFFFF )
						{
							doc_ptr->slice_buff[doc_ptr->slice_len++] = (unsigned char)(conv_val & 0xFF);
							doc_ptr->slice_buff[doc_ptr->slice_len++] = (unsigned char)((conv_val >> 8) & 0xFF);
						}
						else
						{
							doc_ptr->slice_buff[doc_ptr->slice_len++] = (ounsigned char)0x20;
						}

						doc_ptr->pref_cursor++;
						continue;
					}
					else*/
					//{
						tmp_chr = __xmlp_unescape_seq( esc_str, esc_len );
						if ( tmp_chr != '\0' )
						{
							doc_ptr->slice_len -= (esc_len - 1);
							doc_ptr->slice_buff[doc_ptr->slice_len++] = tmp_chr;
							doc_ptr->pref_cursor++;
							
							esc_str	= NULL;
							esc_len = 0;
							continue; // new round
						}
					//}
				}
			}
		}

#if defined(__XMLP_REFINE_INDENT_FORMAT__)
		if ( ch_tmp == 0x0D )
		{
			doc_ptr->pref_cursor++;
			continue;
		}
		if ( ch_tmp == 0x0A )
			ch_tmp = 0x20;
#endif	// #if defined(__XMLP_REFINE_INDENT_FORMAT__)

		if ( doc_ptr->slice_len > limit_len )
		{
			__xmlp_set_error( doc_ptr, xmlp_error_too_long );
			goto __FAILED;
		}

		if ( doc_ptr->slice_len >= doc_ptr->slice_buff_size )
		{
			ret_val = __xmlp_resize_slice_buffer( doc_ptr );
			if ( 0 == ret_val )
			{
				__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
				goto __FAILED;
			}
		}

		if ( ch_tmp == ch_end )
		{
			if ( 0 != inside_tag )
			{
				// fill last character
				doc_ptr->slice_buff[doc_ptr->slice_len++] = ch_end;
				doc_ptr->pref_cursor++;
			}
			break;
		}
		else
		{
			doc_ptr->slice_buff[doc_ptr->slice_len++] = ch_tmp;
			doc_ptr->pref_cursor++;
		}
	}

	doc_ptr->slice_buff[doc_ptr->slice_len] = '\0';
	doc_ptr->slice_buff[doc_ptr->slice_len + 1] = '\0';
	doc_ptr->slice_is_tag = inside_tag;

#if defined(__XMLP_DEBUG__)
	XMLP_TRACE( "[XMLP]: slice buffer => \"%s\"\n", doc_ptr->slice_buff );
#endif	// #if defined(__XMLP_DEBUG__)

	__xmlp_set_state( doc_ptr, xmlp_state_forward );
    return 1;

__FINISH:
	__xmlp_set_state( doc_ptr, xmlp_state_finish );
	return 1;

__FAILED:
	__xmlp_clear_slice_buffer( doc_ptr );
	__xmlp_set_state( doc_ptr, xmlp_state_error );
	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_forward
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_on_state_forward( xmlp_document_ptr_t doc_ptr )
{
	unsigned char * tag_buff;   // tags are UTF8 encoding

	XMLP_ASSERT( NULL != doc_ptr );
	XMLP_ASSERT( doc_ptr->slice_len > 0 );

	tag_buff = (unsigned char *)doc_ptr->slice_buff;
	if ( 0 != doc_ptr->slice_is_tag )
	{
		if ( tag_buff[0] != '<' )
		{
			__xmlp_set_error( doc_ptr, xmlp_error_format );
			__xmlp_set_state( doc_ptr, xmlp_state_error );
			return 0;
		}

		tag_buff = __xmlp_utf8_skip_ws( (unsigned char *)&tag_buff[1], doc_ptr->slice_len - 1 );
		if ( tag_buff[0] == '>' )	// <>, empty tag
		{
			__xmlp_set_state( doc_ptr, xmlp_state_slice );
		}
		if ( 0 != XMLP_IS_ALPHA(tag_buff[0]) )			// <TAG> or <TAG/>
		{
			__xmlp_set_state( doc_ptr, xmlp_state_type_elem_beg );
		}
		else if ( tag_buff[0] == '/' )							// </TAG>
		{
			__xmlp_set_state( doc_ptr, xmlp_state_type_elem_end );
		}
		else if ( tag_buff[0] == '?')							// <?XML?>
		{
			__xmlp_set_state( doc_ptr, xmlp_state_type_decl );
		}
#if defined(__XMLP_SUPPORT_NODE_DOC_TYPE__)
		else if ( 0 == __xmlp_utf8_strncmp_ic((unsigned char *)tag_buff, (unsigned char *)"!DOCTYPE", 8) )	// <!DOCTYPE>
		{
			__xmlp_set_state( doc_ptr, xmlp_state_doc_type );
		}
#endif	// #if defined(__XMLP_SUPPORT_NODE_DOC_TYPE__)
#if defined(__XMLP_SUPPORT_NODE_REMARK__)
		else if ( 0 == __xmlp_utf8_strncmp_ic((unsigned char *)tag_buff, (unsigned char *)"!--", 3) )		// <!-- -->
		{
			__xmlp_set_state( doc_ptr, xmlp_state_type_remark );
		}
#endif	// #if defined(__XMLP_SUPPORT_NODE_REMARK__)
#if defined(__XMLP_SUPPORT_NODE_CDATA__)
		else if ( 0 == __xmlp_utf8_strncmp_ic(&tag_buff[0], (unsigned char *)"![CDATA[", 8) )	// <![CDATA[ ]]>
		{
			__xmlp_set_state( doc_ptr, xmlp_state_type_cdata );
		}
#endif	// #if defined(__XMLP_SUPPORT_NODE_CDATA__)
		else													// Unknown
		{
			__xmlp_set_state( doc_ptr, xmlp_state_type_unknown );
		}
	}
    else														// Text data
    {
		__xmlp_set_state( doc_ptr, xmlp_state_type_text );
    }

	return 1;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_beg
* DESCRIPTION
*	<!DOCTYPE xxx yyy zzz/>
*************************************************************************/
#if defined(__XMLP_SUPPORT_NODE_DOC_TYPE__)
static unsigned long __xmlp_on_state_type_doc_type( xmlp_document_ptr_t doc_ptr )
{
	XMLP_ASSERT( NULL != doc_ptr );
	XMLP_ASSERT( doc_ptr->slice_len > 2 );
	XMLP_ASSERT( doc_ptr->slice_buff[0] == '<' );
	
	// "<!DOCTYPE>" is only allowed to be appearing on the top level of tree.
	if ( doc_ptr->level != 0 || NULL != doc_ptr->root_node )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_format );
		__xmlp_set_state( doc_ptr, xmlp_state_error );
		return 0;
	}
	else
	{
		__xmlp_set_state( doc_ptr, xmlp_state_slice );	// next tag, please
		return 1;
	}
}
#endif	// #if defined(__XMLP_SUPPORT_NODE_DOC_TYPE__)

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_beg
* DESCRIPTION
*	<?XML xxx yyy zzz?>
*************************************************************************/
static unsigned long __xmlp_on_state_type_decl( xmlp_document_ptr_t doc_ptr )
{
	unsigned char *	tag_name;
	unsigned long	tag_len;
	unsigned long	ret_val;

	xmlp_node_ptr_t	node_ptr = NULL;
	unsigned char *	slice_buff = NULL;
	unsigned long	slice_len = 0;
	unsigned long	ws_cnt = 0;
	
	XMLP_ASSERT( NULL != doc_ptr );
	XMLP_ASSERT( doc_ptr->slice_len > 2 );
	XMLP_ASSERT( doc_ptr->slice_buff[0] == '<' );

	if ( doc_ptr->slice_buff[doc_ptr->slice_len - 1] != '>' ||
		 doc_ptr->slice_buff[doc_ptr->slice_len - 2] != '?' )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_format );
		goto __FAILED;
	}

	// The nodes which in "<!xxx>" style are only allowed to be appearing on the top level of tree.
	if ( doc_ptr->level != 0 || NULL != doc_ptr->root_node )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_format );
		goto __FAILED;
	}

	slice_buff	= doc_ptr->slice_buff + 1;
	slice_len	= doc_ptr->slice_len - 1;
	
	ws_cnt = __xmlp_utf8_count_ws( (unsigned char *)slice_buff, slice_len );
	slice_buff	+= ws_cnt;
	slice_len	-= ws_cnt;
	
	if ( slice_buff[0] != '?' )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_format );
		goto __FAILED;
	}
	
	slice_buff	+= 1;	// skip '?'
	slice_len	-= 1;	// skip '?'

	tag_len	 = 0;
	tag_name = __xmlp_tag_name( (unsigned char *)slice_buff, slice_len, &tag_len );
	if ( tag_len == 0 || tag_name[0] == '\0' || 0 == XMLP_IS_ALPHA(tag_name[0]) )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_invalid_name );
		goto __FAILED;
	}
	
	node_ptr = xmlp_node_create();
	if ( NULL == node_ptr )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
		goto __FAILED;
	}

	//node_ptr->level = 0;

	node_ptr->type = xmlp_node_type_decl;
	node_ptr->name = xmlp_string_build_ex( tag_name, tag_len, XMLP_ENCODING_UTF8, XMLP_ENCODING_UTF8 );
	if ( NULL == node_ptr->name )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
		goto __FAILED;
	}

	ret_val = __xmlp_told_event( doc_ptr, xmlp_event_declaration, node_ptr, NULL );
	if ( 0 == ret_val )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_user_error );
		goto __FAILED;
	}

	// if the above operation is succeeded, we should link the node onto document
	if ( NULL != doc_ptr->first_decl )
	{
		doc_ptr->first_decl->prev_sibling = node_ptr;
		node_ptr->next_sibling = doc_ptr->first_decl;
	}
	
	doc_ptr->first_decl = node_ptr;
	doc_ptr->decl_num++;

	if ( doc_ptr->error == xmlp_error_user_break )
	{
		__xmlp_set_state( doc_ptr, xmlp_state_error );
	}
	else
	{
		__xmlp_set_state( doc_ptr, xmlp_state_slice );	// next tag, please
	}

	return 1;

__FAILED:
	xmlp_node_free( node_ptr );
	__xmlp_set_state( doc_ptr, xmlp_state_error );
	return 0;
}

/*************************************************************************
* FUNCTION
*  __widget_text_debug_check
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_link_down_node( xmlp_document_ptr_t doc_ptr, xmlp_node_ptr_t node_ptr )
{
	XMLP_ASSERT( NULL != doc_ptr );

	if ( NULL == node_ptr )
		return 0;

	if ( doc_ptr->root_node == node_ptr )
	{
		doc_ptr->root_node = NULL;
	}

	if ( NULL != node_ptr->parent )
	{
		if ( node_ptr == node_ptr->parent->first_child )
			node_ptr->parent->first_child = node_ptr->next_sibling;
		if ( node_ptr == node_ptr->parent->last_child )
			node_ptr->parent->last_child = node_ptr->prev_sibling;

		node_ptr->child_num -= 1;
	}

	if ( NULL != node_ptr->prev_sibling )
	{
		node_ptr->prev_sibling->next_sibling = node_ptr->next_sibling;
	}

	if ( NULL != node_ptr->next_sibling )
	{
		node_ptr->prev_sibling->next_sibling = node_ptr->next_sibling;
	}

	return 1;
}

/*************************************************************************
* FUNCTION
*  __xmlp_link_node
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_link_up_node( xmlp_document_ptr_t doc_ptr, xmlp_node_ptr_t node_ptr )
{
	xmlp_node_ptr_t	last_node_ptr;

	XMLP_ASSERT( NULL != doc_ptr );

	last_node_ptr = doc_ptr->last_node;
	if ( NULL != last_node_ptr )
	{
		if ( last_node_ptr->type != xmlp_node_type_elem )
		{
			__xmlp_set_error( doc_ptr, xmlp_error_internal );
			goto __FAILED;
		}
		
		if ( node_ptr->level < last_node_ptr->level )
		{
			__xmlp_set_error( doc_ptr, xmlp_error_internal );
			goto __FAILED;
		}
		else if ( node_ptr->level > last_node_ptr->level )
		{
			// new children
			node_ptr->parent = last_node_ptr;
			node_ptr->next_sibling = NULL;

			node_ptr->prev_sibling = last_node_ptr->last_child;
			if ( NULL != last_node_ptr->last_child )
				last_node_ptr->last_child->next_sibling = node_ptr;

			if ( NULL != node_ptr->parent )
			{
				node_ptr->parent->last_child = node_ptr;
				if ( NULL == node_ptr->parent->first_child )
					node_ptr->parent->first_child = node_ptr;

				node_ptr->parent->child_num++;
			}
		}
		else
		{
			// new sibling
			node_ptr->parent = last_node_ptr->parent;
			node_ptr->next_sibling = NULL;

			node_ptr->prev_sibling = last_node_ptr;
			last_node_ptr->next_sibling = node_ptr;

			if ( NULL != node_ptr->parent )
			{
				node_ptr->parent->last_child = node_ptr;
				if ( NULL == node_ptr->parent->first_child )
					node_ptr->parent->first_child = node_ptr;
				
				node_ptr->parent->child_num++;
			}
		}
	}
	
	if ( NULL == doc_ptr->root_node )
	{
		XMLP_ASSERT( node_ptr->level == 0 );
		doc_ptr->root_node = node_ptr;
	}
	
	// Root node can't be itself
	node_ptr->root = (node_ptr != doc_ptr->root_node ? doc_ptr->root_node : NULL);
	return 1;

__FAILED:
	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_on_state_type_elem_beg( xmlp_document_ptr_t doc_ptr )
{
	unsigned char *	tag_name;
	unsigned long	tag_len;
	unsigned long		ret_val;
	unsigned long		is_paired;

	unsigned char *	attr_raw_str;
	unsigned long	attr_raw_len;

	xmlp_node_ptr_t	node_ptr = NULL;

	XMLP_ASSERT( NULL != doc_ptr );
	XMLP_ASSERT( doc_ptr->slice_len > 2 );
	XMLP_ASSERT( doc_ptr->slice_buff[0] == '<' );
	
	if ( doc_ptr->slice_buff[doc_ptr->slice_len - 1] == '>' &&
		 doc_ptr->slice_buff[doc_ptr->slice_len - 2] == '/' )
	{
		is_paired = 0;

		// TODO: fix bug, allow single node
	#if 0
		if ( NULL == doc_ptr->root_node )
		{
			// Root node must be paired
			__xmlp_set_error( doc_ptr, xmlp_error_format );
			goto __FAILED;
		}
	#endif	// #if 0
	}
	else
	{
		is_paired = 1;
	}

	// Multi-root is disallowed
	if ( doc_ptr->level == 0 && NULL != doc_ptr->root_node )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_format );
		goto __FAILED;
	}

	tag_len	 = 0;
	tag_name = __xmlp_tag_name( (unsigned char *)(doc_ptr->slice_buff + 1), doc_ptr->slice_len - 1, &tag_len );
	if ( tag_len == 0 || tag_name[0] == '\0' || 0 == XMLP_IS_ALPHA(tag_name[0]) )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_invalid_name );
		goto __FAILED;
	}

	node_ptr = xmlp_node_create();
	if ( NULL == node_ptr )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
		goto __FAILED;
	}
	
	node_ptr->name = xmlp_string_build_ex( tag_name, tag_len, XMLP_ENCODING_UTF8, XMLP_ENCODING_UTF8 );
	if ( NULL == node_ptr->name )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
		goto __FAILED;
	}

	attr_raw_str = tag_name + tag_len;
	attr_raw_len = doc_ptr->slice_len - ((unsigned char *)attr_raw_str - doc_ptr->slice_buff);

	ret_val = __xmlp_build_attrib_list( doc_ptr, node_ptr, attr_raw_str, attr_raw_len );
	if ( 0 == ret_val )
		goto __FAILED;

	node_ptr->type		= xmlp_node_type_elem;
	node_ptr->level		= doc_ptr->level;
	node_ptr->is_paired	= is_paired;

	ret_val = __xmlp_link_up_node( doc_ptr, node_ptr );
	if ( 0 == ret_val )
		goto __FAILED;

	ret_val = __xmlp_told_event( doc_ptr, xmlp_event_element_begin, node_ptr, NULL );
	if ( 0 == ret_val )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_user_error );
		goto __FAILED;
	}

	// Move down one layer
	doc_ptr->level++;
	doc_ptr->last_node = node_ptr;

	
	if ( doc_ptr->error == xmlp_error_user_break )
	{
		__xmlp_set_state( doc_ptr, xmlp_state_error );
	}
	else
	{
		if ( 0 != node_ptr->is_paired )
		{
			__xmlp_set_state( doc_ptr, xmlp_state_slice );
		}
		else
		{
			__xmlp_set_state( doc_ptr, xmlp_state_type_elem_end );
		}
	}

	return 1;

__FAILED:
	__xmlp_link_down_node( doc_ptr, node_ptr );
	xmlp_node_free( node_ptr );
	__xmlp_set_state( doc_ptr, xmlp_state_error );
	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
static unsigned long	__xmlp_on_state_type_elem_end( xmlp_document_ptr_t doc_ptr )
{
	xmlp_node_ptr_t		last_node_ptr = NULL;
	unsigned long		ret_val;

	unsigned char *		tag_name = NULL;
	unsigned long		tag_len;

	unsigned char *		slice_buff = NULL;
	unsigned long		slice_len = 0;
	unsigned long		ws_cnt = 0;

	XMLP_ASSERT( NULL != doc_ptr );

	// Move up one level
	doc_ptr->level--;

	last_node_ptr = doc_ptr->last_node;
	XMLP_ASSERT( NULL != last_node_ptr );

	if ( 0 != last_node_ptr->is_paired )
	{
		XMLP_ASSERT( doc_ptr->slice_len > 2 );
		XMLP_ASSERT( doc_ptr->slice_buff[0] == '<' );
		//XMLP_ASSERT( doc_ptr->slice_buff[1] == '/' );

		// Get more compatibilities
		slice_buff	= doc_ptr->slice_buff + 1;
		slice_len	= doc_ptr->slice_len - 1;
		
		ws_cnt = __xmlp_utf8_count_ws( (unsigned char *)slice_buff, slice_len );
		slice_buff	+= ws_cnt;
		slice_len	-= ws_cnt;

		if ( slice_buff[0] != '/' )
		{
			__xmlp_set_error( doc_ptr, xmlp_error_format );
			goto __FAILED;
		}

		slice_buff	+= 1;	// skip '/'
		slice_len	-= 1;	// skip '/'
 
		// TODO: compares the node name here, respect the XML standard that said "node names are case sensitive"
		tag_name = __xmlp_tag_name( (unsigned char *)slice_buff, slice_len, &tag_len );
		if ( tag_len == 0 || tag_name[0] == '\0' || 0 == XMLP_IS_ALPHA(tag_name[0]) )
		{
			__xmlp_set_error( doc_ptr, xmlp_error_invalid_name );
			goto __FAILED;
		}

		if ( tag_len != XMLP_STRING_LENGTH( last_node_ptr->name ) )
		{
			__xmlp_set_error( doc_ptr, xmlp_error_format );
			goto __FAILED;
		}

#ifndef	__XMLP_ALLOW_CASE_INSENSITIVE__
		if ( 0 != __xmlp_utf8_strncmp_ic( XMLP_STRING_A(last_node_ptr->name), tag_name, tag_len) )
#else	// #ifndef	__XMLP_ALLOW_CASE_INSENSITIVE__
		if ( 0 != __xmlp_utf8_strncmp( XMLP_STRING_A(last_node_ptr->name), tag_name, tag_len) )
#endif	// #ifndef	__XMLP_ALLOW_CASE_INSENSITIVE__
		{
#if defined(__XMLP_DISCARD_INCOMPLETE_TAG__)
			goto __RETURN;
#else	// #if defined(__XMLP_AUTOFIX_INCOMPLETE_TAG__)
			__xmlp_set_error( doc_ptr, xmlp_error_format );
			goto __FAILED;
#endif	// #if defined(__XMLP_AUTOFIX_INCOMPLETE_TAG__)
		}
	}
	
	ret_val = __xmlp_told_event( doc_ptr, xmlp_event_element_end, last_node_ptr, NULL );
	if ( 0 == ret_val )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_user_error );
		goto __FAILED;
	}
	
	doc_ptr->last_node = last_node_ptr->parent;

__RETURN:
	if ( doc_ptr->error == xmlp_error_user_break )
	{
		__xmlp_set_state( doc_ptr, xmlp_state_error );
	}
	else
	{
		if ( 0 == doc_ptr->level )
		{
			__xmlp_set_state( doc_ptr, xmlp_state_finish );
		}
		else
		{
			__xmlp_set_state( doc_ptr, xmlp_state_slice );
		}
	}

	return 1;

__FAILED:
	__xmlp_set_state( doc_ptr, xmlp_state_error );
	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_on_state_type_text( xmlp_document_ptr_t doc_ptr )
{
	unsigned long		ret_val;
	xmlp_node_ptr_t	node_ptr = NULL;
	xmlp_node_ptr_t	last_node_ptr = NULL;

	XMLP_ASSERT( NULL != doc_ptr );
	XMLP_ASSERT( doc_ptr->slice_len > 0 );

	//XMLP_ASSERT( doc_ptr->slice_buff[0] != '<' );
	
	if ( doc_ptr->level == 0 || NULL == doc_ptr->root_node )
	{
#if defined(__XMLP_ALLOW_HEADING_TEXT__)
		goto __RETURN;
#else	// #if defined(__XMLP_ALLOW_HEADING_TEXT__)
		__xmlp_set_error( doc_ptr, xmlp_error_format );
		goto __FAILED;
#endif	// #if defined(__XMLP_ALLOW_HEADING_TEXT__)
	}

	last_node_ptr = doc_ptr->last_node;
	if ( last_node_ptr == NULL || last_node_ptr->type != xmlp_node_type_elem )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_internal );
		goto __FAILED;
	}

	XMLP_ASSERT( NULL != last_node_ptr->name );

	node_ptr = xmlp_node_create();
	if ( NULL == node_ptr )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
		goto __FAILED;
	}

//	if ( NULL != last_node_ptr->name )
//	{
//		node_ptr->name = last_node_ptr->name;
//		XMLP_STRING_HOLD( last_node_ptr->name );
//	}

	node_ptr->text_type	= xmlp_text_type_string;
	node_ptr->text_data.text = xmlp_string_build_ex( doc_ptr->slice_buff, doc_ptr->slice_len, __XMLP_TEXT_ENCODING__, XMLP_ENCODING_UTF8 );
	if ( NULL == node_ptr->text_data.text )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
		goto __FAILED;
	}

//	XMLP_ASSERT( NULL == last_node_ptr->text );
//	last_node_ptr->text = node_ptr->text;
//	XMLP_STRING_HOLD( node_ptr->text );

	node_ptr->type	= xmlp_node_type_text;
	node_ptr->level	= doc_ptr->level;
	
	ret_val = __xmlp_link_up_node( doc_ptr, node_ptr );
	if ( 0 == ret_val )
		goto __FAILED;
	
	ret_val = __xmlp_told_event( doc_ptr, xmlp_event_text_data, node_ptr, NULL );
	if ( 0 == ret_val )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_user_error );
		goto __FAILED;
	}

__RETURN:

	if ( doc_ptr->error == xmlp_error_user_break )
	{
		__xmlp_set_state( doc_ptr, xmlp_state_error );
	}
	else
	{
		__xmlp_set_state( doc_ptr, xmlp_state_slice );
	}

	return 1;
	
__FAILED:
	__xmlp_link_down_node( doc_ptr, node_ptr );
	xmlp_node_free( node_ptr );
	__xmlp_set_state( doc_ptr, xmlp_state_error );
	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
#if defined(__XMLP_SUPPORT_NODE_CDATA__)
static unsigned long	__xmlp_on_state_type_cdata( xmlp_document_ptr_t doc_ptr )
{
	unsigned long		ret_val;

	unsigned char *		temp_str;
	unsigned long	temp_len;

	xmlp_node_ptr_t	node_ptr = NULL;
	xmlp_node_ptr_t	last_node_ptr = NULL;

	XMLP_ASSERT( NULL != doc_ptr );
	XMLP_ASSERT( doc_ptr->slice_buff[0] == '<' );

	if ( doc_ptr->level == 0 || NULL == doc_ptr->root_node )
	{
#if defined(__XMLP_ALLOW_HEADING_TEXT__)
		goto __RETURN;
#else	// #if defined(__XMLP_ALLOW_HEADING_TEXT__)
		__xmlp_set_error( doc_ptr, xmlp_error_format );
		goto __FAILED;
#endif	// #if defined(__XMLP_ALLOW_HEADING_TEXT__)
	}

	if ( doc_ptr->slice_len < 12/*sizeof("<![CDATA[]]>")*/ )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_format );
		goto __FAILED;
	}

	temp_str = (unsigned char *)(doc_ptr->slice_buff + doc_ptr->slice_len - 3/*sizeof("]]>")*/);
	if ( 0 != __xmlp_utf8_strncmp_ic(temp_str, (unsigned char *)"]]>", 3) )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_format );
		goto __FAILED;
	}

	last_node_ptr = doc_ptr->last_node;
	if ( last_node_ptr == NULL || last_node_ptr->type != xmlp_node_type_elem )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_internal );
		goto __FAILED;
	}
	
	XMLP_ASSERT( NULL != last_node_ptr->name );
	
	node_ptr = xmlp_node_create();
	if ( NULL == node_ptr )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
		goto __FAILED;
	}
	
//	if ( NULL != last_node_ptr->name )
//	{
//		node_ptr->name = last_node_ptr->name;
//		XMLP_STRING_HOLD( last_node_ptr->name );
//	}
	
	temp_str = (unsigned char *)(doc_ptr->slice_buff + 9/*sizeof("<![CDATA[")*/);
	temp_len = doc_ptr->slice_len - 12/*sizeof("<![CDATA[]]>")*/;

#if defined(__XMLP_COPY_CDATA__)
	node_ptr->text_type	= xmlp_text_type_raw_safe;
	node_ptr->text_data.raw.size = temp_len;
	node_ptr->text_data.raw.addr = (void *)XMLP_HEAP_ALLOC( temp_len + 1 );
	if ( NULL == node_ptr->text_data.raw.addr )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_out_of_mem );
		goto __FAILED;
	}

	memcpy( node_ptr->text_data.raw.addr, temp_str, temp_len );
	((unsigned char *)node_ptr->text_data.raw.addr)[temp_len] = '\0';
#else	// #if defined(__XMLP_COPY_CDATA__)
	node_ptr->text_type	= xmlp_text_type_raw_unsafe;
	node_ptr->text_data.raw.len	= temp_len;
	node_ptr->text_data.raw.addr = (void *)temp_str;
#endif	// #if defined(__XMLP_COPY_CDATA__)

	node_ptr->type		= xmlp_node_type_text;
	node_ptr->level		= doc_ptr->level;

	node_ptr->is_paired	= 0;

	ret_val = __xmlp_link_up_node( doc_ptr, node_ptr );
	if ( 0 == ret_val )
		goto __FAILED;
	
	ret_val = __xmlp_told_event( doc_ptr, xmlp_event_text_data, node_ptr, NULL );
	if ( 0 == ret_val )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_user_error );
		goto __FAILED;
	}
	
__RETURN:

	if ( doc_ptr->error == xmlp_error_user_break )
	{
		__xmlp_set_state( doc_ptr, xmlp_state_error );
	}
	else
	{
		__xmlp_set_state( doc_ptr, xmlp_state_slice );
	}

	return 1;
	
__FAILED:
	__xmlp_link_down_node( doc_ptr, node_ptr );
	xmlp_node_free( node_ptr );
	__xmlp_set_state( doc_ptr, xmlp_state_error );
	return 0;
}
#endif	// #if defined(__XMLP_SUPPORT_NODE_CDATA__)

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
#if defined(__XMLP_SUPPORT_NODE_REMARK__)
static unsigned long __xmlp_on_state_type_remark( xmlp_document_ptr_t doc_ptr )
{
	// parse remark node
	__xmlp_set_state( doc_ptr, xmlp_state_slice );	// next tag, please
	return 1;
}
#endif	// #if defined(__XMLP_SUPPORT_NODE_REMARK__)

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_on_state_type_unknwon( xmlp_document_ptr_t doc_ptr )
{
	__xmlp_set_state( doc_ptr, xmlp_state_slice );	// next tag, please
	return 1;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
static unsigned long __xmlp_on_state_error( xmlp_document_ptr_t doc_ptr )
{
	unsigned long	ret_val;

	XMLP_ASSERT( NULL != doc_ptr );

	if ( doc_ptr->error == xmlp_error_user_break )
	{
		__xmlp_set_error( doc_ptr, xmlp_error_OK );
		goto __BREAK;
	}

#if defined(__XMLP_DEBUG__)
	XMLP_TRACE( "[XMLP]: error occured, code -> %d\n", doc_ptr->error );
	XMLP_TRACE( "[XMLP]: dump buffer:\n %s\n", doc_ptr->slice_buff );
#endif	// #if defined(__XMLP_DEBUG__)
	
	ret_val = __xmlp_told_event( doc_ptr, xmlp_event_error, NULL, (void *)doc_ptr->error );
	if ( 0 == ret_val )
	{
		// if user gave "0", denotes the error really occurred.
		__xmlp_set_error( doc_ptr, xmlp_error_user_error );
		goto __BREAK;
	}

	__xmlp_set_error( doc_ptr, xmlp_error_OK );
	__xmlp_set_state( doc_ptr, xmlp_state_slice );	// next tag, please
	return 1;

__BREAK:
	if ( NULL != doc_ptr->first_decl )
	{
		xmlp_node_free_branch( doc_ptr->first_decl );
		doc_ptr->first_decl = NULL;
		doc_ptr->decl_num	= 0;
	}
	if ( NULL != doc_ptr->root_node )
	{
		xmlp_node_free_branch( doc_ptr->root_node );
		doc_ptr->root_node = NULL;
	}
	doc_ptr->last_node = NULL;

	__xmlp_set_state( doc_ptr, xmlp_state_finish );
	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
static unsigned long	__xmlp_on_state_finish( xmlp_document_ptr_t doc_ptr )
{
	XMLP_ASSERT( NULL != doc_ptr );

	__xmlp_told_event( doc_ptr, xmlp_event_finish, NULL, NULL );

	__xmlp_free_slice_buffer( doc_ptr );
	__xmlp_free_prefetch_buffer( doc_ptr );

	doc_ptr->slice_buff_size	= 0;
	doc_ptr->slice_len			= 0;

	doc_ptr->pref_buff_size	= 0;
	doc_ptr->pref_len		= 0;

	doc_ptr->last_node	= NULL;
	doc_ptr->level		= 0;

#if defined(__XMLP_DEBUG__)
	XMLP_TRACE( "[XMLP]: complete\n" );
#endif	// #if defined(__XMLP_DEBUG__)

	__xmlp_set_state( doc_ptr, xmlp_state_opened );
	__xmlp_update_progress( doc_ptr );

	return ( doc_ptr->error != xmlp_error_OK ? 0 : 1 );
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
void xmlp_node_set_paired( xmlp_node_ptr_t node_ptr, unsigned long flag )
{
    if ( NULL != node_ptr )
        node_ptr->is_paired = flag;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
void * xmlp_node_get_user_data( xmlp_node_ptr_t node_ptr )
{
	XMLP_ASSERT( NULL != node_ptr );
	return node_ptr->user_data;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
void xmlp_node_set_user_data( xmlp_node_ptr_t node_ptr, void * user_data, xmlp_free_fptr_t user_free )
{
	XMLP_ASSERT( NULL != node_ptr );

	xmlp_node_free_user_data( node_ptr );

	node_ptr->user_data	= user_data;
	node_ptr->user_free	= user_free;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
void xmlp_node_free_user_data( xmlp_node_ptr_t node_ptr )
{
	if ( NULL != node_ptr && NULL != node_ptr->user_free )
	{
		XMLP_CALL( node_ptr->user_free, (node_ptr->user_data) );
		node_ptr->user_data	= NULL;
		node_ptr->user_free	= NULL;
	}
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
#if defined(__XMLP_DEBUG__)
void xmlp_dump_tree( xmlp_node_ptr_t branch_ptr )
{
	if ( NULL == branch_ptr )
		return;

	if ( branch_ptr->type == xmlp_node_type_elem )
	{
		XMLP_TRACE( "[XMLP]: (%d) <%s>, %d child(s)\n", branch_ptr->level, XMLP_STRING_A(branch_ptr->name), branch_ptr->child_num );
	}
	else if ( branch_ptr->type == xmlp_node_type_text )
	{
		if ( branch_ptr->text_type == xmlp_text_type_string )
		{
			XMLP_TRACE( "[XMLP]: (str)   length = %d\n", XMLP_STRING_LENGTH(branch_ptr->text_data.text) );
		}
		else if ( branch_ptr->text_type == xmlp_text_type_raw_safe || branch_ptr->text_type == xmlp_text_type_raw_unsafe )
		{
			XMLP_TRACE( "[XMLP]: (raw)   addr = 0x%08x, size = %d\n", branch_ptr->text_data.raw.addr, branch_ptr->text_data.raw.size );
		}
	}

	xmlp_dump_tree( branch_ptr->first_child );
	xmlp_dump_tree( branch_ptr->next_sibling );
}
#endif	// #if defined(__XMLP_DEBUG__)

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
xmlp_string_ptr_t xmlp_string_build_a( unsigned char * buff, unsigned long size )
{
	xmlp_string_ptr_t xml_str;

	XMLP_ASSERT( NULL != buff && size > 0 );

	xml_str = (xmlp_string_ptr_t)XMLP_HEAP_ALLOC( sizeof(xmlp_string_t) + size );
	if ( NULL == xml_str )
		return NULL;

	xml_str->ref = 1;
	xml_str->enc = XMLP_ENCODING_UTF8;

	memcpy( (void *)&xml_str->str.a[0], (const void *)buff, size );
	xml_str->str.a[size] = '\0';
	xml_str->len = size;

	return xml_str;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
xmlp_string_ptr_t xmlp_string_build_w( unsigned char * buff, unsigned long size )
{
	xmlp_string_ptr_t xml_str;

	XMLP_ASSERT( NULL != buff && size > 0 );

	xml_str = (xmlp_string_ptr_t)XMLP_HEAP_ALLOC( sizeof(xmlp_string_t) + size );
	if ( NULL == xml_str )
		return NULL;

	xml_str->ref = 1;
	xml_str->enc = XMLP_ENCODING_UCS2;

	memcpy( (void *)&xml_str->str.w[0], (const void *)buff, size );
	xml_str->str.w[(size >> 1)] = '\0';
	xml_str->len = (size >> 1);

	return xml_str;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
xmlp_string_ptr_t xmlp_string_build_ex( unsigned char * buff, unsigned long size, unsigned long dst_enc, unsigned long src_enc )
{
	xmlp_string_ptr_t xml_str = NULL;

	XMLP_ASSERT( NULL != xml_str );
	XMLP_ASSERT( NULL != buff && size > 0 );

	if ( dst_enc == src_enc )
	{
		if ( XMLP_ENCODING_UTF8 == dst_enc )
		{
			return xmlp_string_build_a( buff, size );
		}
		else if ( XMLP_ENCODING_UCS2 == dst_enc )
		{
			return xmlp_string_build_w( buff, size );			
		}
	}
	else
	{
		if ( XMLP_ENCODING_UCS2 == dst_enc && XMLP_ENCODING_UTF8 == src_enc )
		{
			unsigned long chr_count;
			
			chr_count = __xmlp_utf8_char_count( buff, size );
			if ( 0 == chr_count )
				return NULL;
	
			xml_str = (xmlp_string_ptr_t)XMLP_HEAP_ALLOC( sizeof(xmlp_string_t) + ((chr_count + 1) << 1) );
			if ( NULL == xml_str )
				return NULL;

			xml_str->ref = 1;
			xml_str->enc = XMLP_ENCODING_UCS2;
			xml_str->len = __xmlp_utf8_to_ucs2( (void *)&xml_str->str.w[0], ((chr_count + 1) << 1), (void *)buff, size );
		}
	}

	return xml_str;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
void xmlp_string_hold( xmlp_string_ptr_t xml_str )
{
	XMLP_ASSERT( NULL != xml_str );
	xml_str->ref += 1;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
void xmlp_string_free( xmlp_string_ptr_t xml_str )
{
	XMLP_ASSERT( NULL != xml_str );

	xml_str->ref -= 1;
	if ( xml_str->ref == 0 )
	{
		XMLP_HEAP_FREE( xml_str );
	}
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
unsigned long xmlp_string_equal( xmlp_string_ptr_t lhs, xmlp_string_ptr_t rhs )
{
	if ( lhs == rhs )
		return 1;

	XMLP_ASSERT( lhs->enc == rhs->enc );
	
	if ( XMLP_ENCODING_UTF8 == lhs->enc )
	{
		if ( 0 == __xmlp_utf8_strncmp(&lhs->str.a[0], &rhs->str.a[0], XMLP_MIN(lhs->len, rhs->len)) )
			return 1;
	}
	else if ( XMLP_ENCODING_UCS2 == lhs->enc )
	{
		if ( 0 == __xmlp_ucs2_strncmp(&lhs->str.w[0], &rhs->str.w[0], XMLP_MIN(lhs->len, rhs->len)) )
			return 1;
	}

	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
unsigned long xmlp_string_equal_ex( xmlp_string_ptr_t lhs, void * rhs )
{
	if ( XMLP_ENCODING_UTF8 == lhs->enc )
	{
		if ( 0 == __xmlp_utf8_strncmp(&lhs->str.a[0], (unsigned char *)rhs, lhs->len) )
			return 1;
	}
	else if ( XMLP_ENCODING_UCS2 == lhs->enc )
	{
		if ( 0 == __xmlp_ucs2_strncmp(&lhs->str.w[0], (unsigned short *)rhs, lhs->len) )
			return 1;
	}
	
	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
unsigned long xmlp_string_equal_ic( xmlp_string_ptr_t lhs, xmlp_string_ptr_t rhs )
{
	if ( lhs == rhs )
		return 1;

	XMLP_ASSERT( lhs->enc == rhs->enc );
	
	if ( XMLP_ENCODING_UTF8 == lhs->enc )
	{
		if ( 0 == __xmlp_utf8_strncmp_ic(&lhs->str.a[0], &rhs->str.a[0], XMLP_MIN(lhs->len, rhs->len)) )
			return 1;
	}
	else if ( XMLP_ENCODING_UCS2 == lhs->enc )
	{
		if ( 0 == __xmlp_ucs2_strncmp_ic(&lhs->str.w[0], &rhs->str.w[0], XMLP_MIN(lhs->len, rhs->len)) )
			return 1;
	}
	
	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
unsigned long xmlp_string_equal_ic_ex( xmlp_string_ptr_t lhs, void * rhs )
{
	if(NULL == lhs || NULL == rhs)
		return 0;
	
	if ( XMLP_ENCODING_UTF8 == lhs->enc )
	{
		if(strlen((const char *)rhs) != lhs->len)
			return 0;
		if ( 0 == __xmlp_utf8_strncmp_ic(&lhs->str.a[0], (unsigned char *)rhs, lhs->len) )
			return 1;
	}
	else if ( XMLP_ENCODING_UCS2 == lhs->enc )
	{
		if ( 0 == __xmlp_ucs2_strncmp_ic(&lhs->str.w[0], (unsigned short *)rhs, lhs->len) )
			return 1;
	}
	
	return 0;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
xmlp_node_ptr_t xmlp_util_find_node( xmlp_node_ptr_t branch_ptr, xmlp_string_ptr_t name, unsigned long ignore_case, unsigned long max_depth )
{
	if ( XMLP_ENCODING_UTF8 != XMLP_STRING_ENC(name) )
		return NULL;
	
	return xmlp_util_find_node_ex( branch_ptr, XMLP_STRING_A(name), ignore_case, max_depth );
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
xmlp_node_ptr_t xmlp_util_find_node_ex( xmlp_node_ptr_t branch_ptr, unsigned char * name, unsigned long ignore_case, unsigned long max_depth )
{
	xmlp_node_ptr_t i;

	if ( NULL == branch_ptr )
		return NULL;

	XMLP_NODE_FOREACH_NODE( i, branch_ptr )
	{
		if ( 0 == ignore_case )
		{
			if ( 1 == xmlp_string_equal_ex( i->name, (void *)name ) )
				return i;
		}
		else
		{
			if ( 1 == xmlp_string_equal_ic_ex( i->name, (void *)name ) )
				return i;			
		}

		if ( max_depth > 0 )
		{
			i = xmlp_util_find_node_ex( i->first_child, name, ignore_case, max_depth - 1 );
			if ( NULL != i )
				return i;
			else
				return NULL;
		}
	}
	
	return NULL;
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
xmlp_attrib_ptr_t xmlp_util_find_attr( xmlp_node_ptr_t node_ptr, xmlp_string_ptr_t name, unsigned long ignore_case )
{
	if ( XMLP_ENCODING_UTF8 != XMLP_STRING_ENC(name) )
		return NULL;
	
	return xmlp_util_find_attr_ex( node_ptr, XMLP_STRING_A(name), ignore_case );
}

/*************************************************************************
* FUNCTION
*  __xmlp_on_state_decl_end
* DESCRIPTION
*
*************************************************************************/
xmlp_attrib_ptr_t xmlp_util_find_attr_ex( xmlp_node_ptr_t node_ptr, unsigned char * name, unsigned long ignore_case )
{
	xmlp_attrib_ptr_t i;

	XMLP_NODE_FOREACH_ATTRIB( i, node_ptr )
	{
		if ( 0 == ignore_case )
		{
			if ( 1 == xmlp_string_equal_ex( i->name, (void *)name ) )
				return i;
		}
		else
		{
			if ( 1 == xmlp_string_equal_ic_ex( i->name, (void *)name ) )
				return i;			
		}
	}
	
	return NULL;
}

